home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / utilities / isfast / libpdb / pdb.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  15.4 KB  |  695 lines

  1. /*****************************************************************************
  2.  * pdb - routines for maintaining a database of performance information
  3.  *
  4.  * Porting Notes:
  5.  *
  6.  *    ANSI C (including library routines specified by the standard) is
  7.  *    used throughout.
  8.  *
  9.  *    The routines GetDBFileName() and GetDefaultMachineName() should be
  10.  *    modified when porting to non-UNIX systems.
  11.  *
  12.  *    GetClock() should be modified when porting to non-SVR4 systems, or
  13.  *    to systems whose "double" type has fewer bits of mantissa than
  14.  *    specified by IEEE 754.
  15.  *
  16.  * History:
  17.  *
  18.  *    1.0    9/93    akin    Written.  See accompanying README for
  19.  *                rationale and examples.
  20.  *****************************************************************************/
  21.  
  22.  
  23.  
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <sys/time.h>    /* for gettimeofday, used by GetClock */
  28. #include "pdb.h"
  29.  
  30.  
  31.  
  32. typedef struct HashNodeS
  33.     {
  34.     struct HashNodeS*    next;
  35.     char*            machineName;
  36.     char*            applicationName;
  37.     char*            benchmarkName;
  38.     double            rate;
  39.     char            storage[1];
  40.     } HashNodeT;
  41. static HashNodeT** HashTable = NULL;
  42. #define HASH_STEP(hash,c) (hash)+=(c)
  43. #define HASH_TABLE_SIZE 64
  44. #define HASH_MODULUS(hash) (hash)&=(HASH_TABLE_SIZE-1)
  45.  
  46.  
  47.  
  48. static int    Dirty = 0;
  49.  
  50.  
  51.  
  52. #define LINE_MAX 1024
  53.  
  54.  
  55.  
  56. static double      ChooseRunTime        (void);
  57. static pdbStatusT DumpHashTable        (FILE*        dbFile);
  58. static void      FinalizeHashTable    (void);
  59. static double      GetClock        (void);
  60. static void      GetDBFileName        (char*        name);
  61. static void      GetDefaultMachineName    (char*        name);
  62. static pdbStatusT InitializeHashTable    (void);
  63. static pdbStatusT InsertHashNode    (const char*    machineName,
  64.                      int        machineNameLength,
  65.                      const char*    applicationName,
  66.                      int        applicationNameLength,
  67.                      const char*    benchmarkName,
  68.                      int        benchmarkNameLength,
  69.                      double        rate,
  70.                      unsigned    hash);
  71. static pdbStatusT LoadHashTable        (FILE*        dbFile);
  72. static pdbStatusT LookupHashNode    (HashNodeT**    nodeP,
  73.                      const char*    machineName,
  74.                      const char*    applicationName,
  75.                      const char*    benchmarkName,
  76.                      int        createWhenMissing);
  77. static double      WaitForTick        (void);
  78.  
  79.  
  80.  
  81. /*****************************************************************************
  82.  * ChooseRunTime - select an appropriate runtime for benchmarks
  83.  *****************************************************************************/
  84.  
  85. static double
  86. ChooseRunTime(void)
  87.     {
  88.     register double start;
  89.     register double finish;
  90.     double        runTime;
  91.  
  92.     start = GetClock();
  93.  
  94.     /* Wait for next tick: */
  95.     while ((finish = GetClock()) == start)
  96.         ;
  97.     
  98.     /* Run for 100 ticks, clamped to [0.5 sec, 5.0 sec]: */
  99.     runTime = 100.0 * (finish - start);
  100.     if (runTime < 0.5)
  101.         runTime = 0.5;
  102.     else if (runTime > 5.0)
  103.         runTime = 5.0;
  104.  
  105.     return runTime;
  106.     }
  107.  
  108.  
  109.  
  110. /*****************************************************************************
  111.  * DumpHashTable - write contents of hash table to database file
  112.  *****************************************************************************/
  113.  
  114. static pdbStatusT
  115. DumpHashTable(FILE* dbFile)
  116.     {
  117.     register int        i;
  118.     register HashNodeT*    n;
  119.  
  120.     if (!HashTable)
  121.         return PDB_NOT_OPEN;
  122.  
  123.     for (i = 0; i < HASH_TABLE_SIZE; ++i)
  124.         for (n = HashTable[i]; n; n = n->next)
  125.             fprintf(dbFile, "%s\t%s\t%s\t%g\n",
  126.                 n->machineName,
  127.                 n->applicationName,
  128.                 n->benchmarkName,
  129.                 n->rate);
  130.  
  131.     return PDB_NO_ERROR;
  132.     }
  133.  
  134.  
  135.  
  136. /*****************************************************************************
  137.  * FinalizeHashTable - deallocate all storage used by the hash table
  138.  *****************************************************************************/
  139.  
  140. static void
  141. FinalizeHashTable(void)
  142.     {
  143.     register int        i;
  144.     register HashNodeT*    h;
  145.     register HashNodeT*    next;
  146.  
  147.     if (!HashTable)
  148.         return;
  149.  
  150.     for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
  151.         for (h = HashTable[i]; h; h = next)
  152.             {
  153.             next = h->next;
  154.             free(h);
  155.             }
  156.     
  157.     free(HashTable);
  158.     HashTable = NULL;
  159.     }
  160.  
  161.  
  162.  
  163. /*****************************************************************************
  164.  * GetClock - get current time (expressed in microseconds)
  165.  *****************************************************************************/
  166.  
  167. static double
  168. GetClock(void)
  169.     {
  170.     struct timeval t;
  171.  
  172.     gettimeofday(&t);
  173.  
  174.     return (double) t.tv_sec + (double) t.tv_usec * 1E-6;
  175.     }
  176.  
  177.  
  178.  
  179. /*****************************************************************************
  180.  * GetDBFileName - get full pathname of performance database file
  181.  *****************************************************************************/
  182.  
  183. static void
  184. GetDBFileName(char* name)
  185.     {
  186.     char* home;
  187.  
  188.     if (home = getenv("HOME"))
  189.         strcpy(name, home);
  190.     else
  191.         name[0] = '\0';
  192.     strcat(name, "/.pdb");
  193.     }
  194.  
  195.  
  196.  
  197. /*****************************************************************************
  198.  * GetDefaultMachineName - return name of "current" machine
  199.  *****************************************************************************/
  200.  
  201. static void
  202. GetDefaultMachineName(char* name)
  203.     {
  204.     char* display;
  205.  
  206.     if (display = getenv("DISPLAY"))
  207.         strcpy(name, display);
  208.     else
  209.         strcpy(name, ":0");
  210.     }
  211.  
  212.  
  213.  
  214. /*****************************************************************************
  215.  * InitializeHashTable - allocate memory for hash table and initialize it
  216.  *****************************************************************************/
  217.  
  218. static pdbStatusT
  219. InitializeHashTable(void)
  220.     {
  221.     register int i;
  222.  
  223.     HashTable = (HashNodeT**)
  224.         malloc(HASH_TABLE_SIZE * sizeof(HashNodeT*));
  225.     if (!HashTable)
  226.         return PDB_OUT_OF_MEMORY;
  227.  
  228.     for (i = HASH_TABLE_SIZE - 1; i >= 0; --i)
  229.         HashTable[i] = NULL;
  230.     
  231.     return PDB_NO_ERROR;
  232.     }
  233.  
  234.  
  235.  
  236. /*****************************************************************************
  237.  * InsertHashNode - place key and data in a selected bucket of the hash table
  238.  *
  239.  * Note:  Does not check for duplicates.
  240.  *      String length arguments *include* the zero byte at the end of the
  241.  *        string; e.g. "abc" would have length 4.
  242.  *****************************************************************************/
  243.  
  244. static pdbStatusT
  245. InsertHashNode
  246.     (
  247.     const char*    machineName,
  248.     int        machineNameLength,
  249.     const char*    applicationName,
  250.     int        applicationNameLength,
  251.     const char*    benchmarkName,
  252.     int        benchmarkNameLength,
  253.     double    rate,
  254.     unsigned    hash
  255.     )
  256.     {
  257.     register HashNodeT*    n;
  258.  
  259.     if (!HashTable)
  260.         return PDB_NOT_OPEN;
  261.  
  262.     n = (HashNodeT*) malloc(sizeof(HashNodeT) + machineNameLength
  263.         + applicationNameLength + benchmarkNameLength);
  264.     if (!n)
  265.         return PDB_OUT_OF_MEMORY;
  266.  
  267.     n->machineName = n->storage;
  268.     memcpy(n->machineName, machineName, machineNameLength);
  269.     n->applicationName = n->machineName + machineNameLength;
  270.     memcpy(n->applicationName, applicationName, applicationNameLength);
  271.     n->benchmarkName = n->applicationName + applicationNameLength;
  272.     memcpy(n->benchmarkName, benchmarkName, benchmarkNameLength);
  273.     n->rate = rate;
  274.  
  275.     n->next = HashTable[hash];
  276.     HashTable[hash] = n;
  277.  
  278.     return PDB_NO_ERROR;
  279.     }
  280.  
  281.  
  282.  
  283. /*****************************************************************************
  284.  * LoadHashTable - load hash table with contents of performance database file
  285.  *****************************************************************************/
  286.  
  287. static pdbStatusT
  288. LoadHashTable(FILE* dbFile)
  289.     {
  290.     char        line[LINE_MAX];
  291.     pdbStatusT    error = PDB_NO_ERROR;
  292.  
  293.     if (!HashTable)
  294.         return PDB_NOT_OPEN;
  295.  
  296.     while (fgets(line, sizeof(line), dbFile))
  297.         {
  298.         char*            machineName;
  299.         char*            applicationName;
  300.         char*            benchmarkName;
  301.         int            machineNameLength;
  302.         int            applicationNameLength;
  303.         int            benchmarkNameLength;
  304.         double            rate;
  305.         register char*        p;
  306.         register int        c;
  307.         register unsigned    hash;
  308.  
  309.  
  310.         /* We use open code here, to minimize app startup time... */
  311.  
  312.         p = line;
  313.         c = *p++;
  314.         hash = 0;
  315.  
  316.         /* Skip whitespace before machine name: */
  317.         while (c == ' ' || c == '\t')
  318.             c = *p++;
  319.         if (c == '\n')
  320.             {
  321.             error |= PDB_SYNTAX_ERROR;
  322.             continue;
  323.             }
  324.  
  325.         /* Scan machine name, get length and hash value: */
  326.         machineName = p - 1;
  327.         while (c != ' ' && c != '\t' && c != '\n')
  328.             {
  329.             HASH_STEP(hash, c);
  330.             c = *p++;
  331.             }
  332.         p[-1] = '\0';
  333.         machineNameLength = p - machineName; /* includes '\0' */
  334.  
  335.         /* Skip whitespace before application name: */
  336.         while (c == ' ' || c == '\t')
  337.             c = *p++;
  338.         if (c == '\n')
  339.             {
  340.             error |= PDB_SYNTAX_ERROR;
  341.             continue;
  342.             }
  343.  
  344.         /* Scan application name, get length and hash: */
  345.         applicationName = p - 1;
  346.         while (c != ' ' && c != '\t' && c != '\n')
  347.             {
  348.             HASH_STEP(hash, c);
  349.             c = *p++;
  350.             }
  351.         p[-1] = '\0';
  352.         applicationNameLength = p - applicationName;
  353.         
  354.         /* Skip whitespace before benchmark name: */
  355.         while (c == ' ' || c == '\t')
  356.             c = *p++;
  357.         if (c == '\n')
  358.             {
  359.             error |= PDB_SYNTAX_ERROR;
  360.             continue;
  361.             }
  362.  
  363.         /* Scan benchmark name, get length and hash: */
  364.         benchmarkName = p - 1;
  365.         while (c != ' ' && c != '\t' && c != '\n')
  366.             {
  367.             HASH_STEP(hash, c);
  368.             c = *p++;
  369.             }
  370.         p[-1] = '\0';
  371.         benchmarkNameLength = p - benchmarkName;
  372.  
  373.         /* Finally, get the rate: */
  374.         rate = strtod(p, NULL);
  375.         if (rate <= 0.0)
  376.             error |= PDB_SYNTAX_ERROR;    /* probably */
  377.  
  378.  
  379.         HASH_MODULUS(hash);
  380.  
  381.  
  382.         /* Note that we don't weed out any duplicates here... */
  383.  
  384.         error |= InsertHashNode(
  385.             machineName, machineNameLength,
  386.             applicationName, applicationNameLength,
  387.             benchmarkName, benchmarkNameLength,
  388.             rate, hash);
  389.         }
  390.  
  391.     return error;
  392.     }
  393.  
  394.  
  395.     
  396. /*****************************************************************************
  397.  * LookupHashNode - find key/data node in hash table, or insert it if needed
  398.  *****************************************************************************/
  399.  
  400. static pdbStatusT
  401. LookupHashNode
  402.     (
  403.     HashNodeT**    nodeP,
  404.     const char*    machineName,
  405.     const char*    applicationName,
  406.     const char*    benchmarkName,
  407.     int        createWhenMissing
  408.     )
  409.     {
  410.     char            defaultMachineName[LINE_MAX];
  411.     register const char*    p;
  412.     register int        c;
  413.     register unsigned    hash;
  414.     register HashNodeT*    node;
  415.     int            machineNameLength;
  416.     int            applicationNameLength;
  417.     int            benchmarkNameLength;
  418.     pdbStatusT        error;
  419.  
  420.     if (!HashTable)
  421.         return PDB_NOT_OPEN;
  422.  
  423.     if (!machineName)
  424.         {
  425.         GetDefaultMachineName(defaultMachineName);
  426.         machineName = defaultMachineName;
  427.         }
  428.  
  429.     hash = 0;
  430.  
  431.     for (p = machineName, c = *p++; c; c = *p++)
  432.         HASH_STEP(hash, c);
  433.     machineNameLength = p - machineName;    /* includes '\0' at end */
  434.     for (p = applicationName, c = *p++; c; c = *p++)
  435.         HASH_STEP(hash, c);
  436.     applicationNameLength = p - applicationName;
  437.     for (p = benchmarkName, c = *p++; c; c = *p++)
  438.         HASH_STEP(hash, c);
  439.     benchmarkNameLength = p - benchmarkName;
  440.     
  441.     HASH_MODULUS(hash);
  442.  
  443.     for (node = HashTable[hash]; node; node = node->next)
  444.         if (!strcmp(node->machineName, machineName)
  445.          && !strcmp(node->applicationName, applicationName)
  446.          && !strcmp(node->benchmarkName, benchmarkName))
  447.             {
  448.             *nodeP = node;
  449.             return PDB_NO_ERROR;
  450.             }
  451.  
  452.     if (createWhenMissing)
  453.         {
  454.         error = InsertHashNode(
  455.             machineName, machineNameLength,
  456.             applicationName, applicationNameLength,
  457.             benchmarkName, benchmarkNameLength,
  458.             0.0, hash);
  459.         *nodeP = HashTable[hash];
  460.         return error;
  461.         }
  462.     else
  463.         return PDB_NOT_FOUND;
  464.     }
  465.  
  466.  
  467.  
  468. /*****************************************************************************
  469.  * WaitForTick - wait for beginning of next system clock tick; return the time
  470.  *****************************************************************************/
  471.  
  472. static double
  473. WaitForTick(void)
  474.     {
  475.     register double start;
  476.     register double current;
  477.  
  478.     start = GetClock();
  479.  
  480.     /* Wait for next tick: */
  481.     while ((current = GetClock()) == start)
  482.         ;
  483.  
  484.     /* Start timing: */
  485.     return current;
  486.     }
  487.  
  488.  
  489.  
  490. /*****************************************************************************
  491.  * pdbClose - write perf data to database file if necessary, then clean up
  492.  *****************************************************************************/
  493.  
  494. pdbStatusT
  495. pdbClose(void)
  496.     {
  497.     pdbStatusT    error = PDB_NO_ERROR;
  498.     char        dbFileName[FILENAME_MAX];
  499.     FILE*        dbFile;
  500.  
  501.     if (!HashTable)
  502.         return PDB_NOT_OPEN;
  503.  
  504.     if (Dirty)
  505.         {
  506.         GetDBFileName(dbFileName);
  507.  
  508.         if (dbFile = fopen(dbFileName, "w"))
  509.             {
  510.             error = DumpHashTable(dbFile);
  511.             fclose(dbFile);
  512.             }
  513.         else
  514.             error = PDB_CANT_WRITE;
  515.  
  516.         Dirty = 0;
  517.         }
  518.  
  519.     FinalizeHashTable();
  520.  
  521.     return error;
  522.     }
  523.  
  524.  
  525.  
  526. /*****************************************************************************
  527.  * pdbMeasureRate - measure number of caller's operations performed per second
  528.  *****************************************************************************/
  529.  
  530. pdbStatusT
  531. pdbMeasureRate
  532.     (
  533.     pdbCallbackT    initialize,
  534.     pdbCallbackT    operation,
  535.     pdbCallbackT    finalize,
  536.     double*        rate
  537.     )
  538.     {
  539.     register double    runTime;
  540.     register long    reps;
  541.     register long    i;
  542.     double        finalizeTime;
  543.     register double    start;
  544.     register double current;
  545.     double        overhead;
  546.     double        shortRunTime;
  547.  
  548.  
  549.     if (!operation)
  550.         {
  551.         *rate = 0.0;
  552.         return PDB_NO_ERROR;
  553.         }
  554.  
  555.  
  556.     /* Select a run time that's appropriate for our timer resolution: */
  557.     runTime = ChooseRunTime();
  558.     shortRunTime = 0.5 * runTime;
  559.  
  560.  
  561.     /* Measure approximate overhead for finalization and timing routines: */
  562.     if (initialize)
  563.         (*initialize)();
  564.     reps = 0;
  565.     start = WaitForTick();
  566.     do
  567.         {
  568.         if (finalize)
  569.             (*finalize)();
  570.         ++reps;
  571.         } while ((current = GetClock()) < start + shortRunTime);
  572.     overhead = (current - start) / (double) reps;
  573.  
  574.  
  575.     /*
  576.      * Measure successively larger batches of operations until we find
  577.      * one that's long enough to meet our runtime target:
  578.      */
  579.     reps = 1;
  580.     for (;;)
  581.         {
  582.         if (initialize)
  583.             (*initialize)();
  584.  
  585.         start = WaitForTick();
  586.  
  587.         for (i = reps; i > 0; --i)
  588.             (*operation)();
  589.  
  590.         if (finalize)
  591.             (*finalize)();
  592.  
  593.         current = GetClock();
  594.         if (current >= start + runTime + overhead)
  595.             break;
  596.  
  597.         /* Try to reach runtime target in one fell swoop: */
  598.         if (current > start)
  599.             reps *= 0.5 + runTime / (current - start - overhead);
  600.         else
  601.             reps *= 2;
  602.         }
  603.  
  604.     /* Subtract overhead to determine the final operation rate: */
  605.     *rate = (double) reps / (current - start - overhead);
  606.     return PDB_NO_ERROR;
  607.     }
  608.  
  609.  
  610.  
  611. /*****************************************************************************
  612.  * pdbOpen - open perf database file, load contents into memory
  613.  *****************************************************************************/
  614.  
  615. pdbStatusT
  616. pdbOpen(void)
  617.     {
  618.     pdbStatusT    error;
  619.     char        dbFileName[FILENAME_MAX];
  620.     FILE*        dbFile;
  621.  
  622.     if (HashTable)
  623.         return PDB_ALREADY_OPEN;
  624.  
  625.     if (error = InitializeHashTable())
  626.         return error;
  627.  
  628.     /* If the database file can be read, load its contents: */
  629.     GetDBFileName(dbFileName);
  630.     if (dbFile = fopen(dbFileName, "r"))
  631.         {
  632.         error = LoadHashTable(dbFile);
  633.         fclose(dbFile);
  634.         }
  635.  
  636.     /* The database is "clean" unless pdbWriteRate() is called: */
  637.     Dirty = 0;
  638.  
  639.     return error;
  640.     }
  641.  
  642.  
  643.  
  644. /*****************************************************************************
  645.  * pdbReadRate - return performance for a given machine, app, and benchmark
  646.  *****************************************************************************/
  647.  
  648. pdbStatusT
  649. pdbReadRate
  650.     (
  651.     const char*        machineName,
  652.     const char*        applicationName,
  653.     const char*        benchmarkName,
  654.     double*        rate
  655.     )
  656.     {
  657.     HashNodeT*    node;
  658.     pdbStatusT    error;
  659.  
  660.     error = LookupHashNode(&node, machineName, applicationName,
  661.         benchmarkName, 0); /* don't create node if it's not present */
  662.     if (!error)
  663.         *rate = node->rate;
  664.  
  665.     return error;
  666.     }
  667.  
  668.  
  669.  
  670. /*****************************************************************************
  671.  * pdbWriteRate - save performance data for a given machine, app, and benchmark
  672.  *****************************************************************************/
  673.  
  674. pdbStatusT
  675. pdbWriteRate
  676.     (
  677.     const char*        machineName,
  678.     const char*        applicationName,
  679.     const char*        benchmarkName,
  680.     const double    rate
  681.     )
  682.     {
  683.     HashNodeT*    node;
  684.     pdbStatusT    error;
  685.  
  686.     Dirty = 1;
  687.  
  688.     error = LookupHashNode(&node, machineName, applicationName,
  689.        benchmarkName, 1); /* create node if it's not already in table */
  690.     if (!error)
  691.         node->rate = rate;
  692.  
  693.     return error;
  694.     }
  695.